/* ========================================
---------- for integer multiply ----------
i_round_q must be 0, i_msub_q could be any value
i_sign : 1/0 => signed/unsigned
 (i_dotp, i_mode8) =>
0,0 : 16x16
0,1 : two 8x8
1,1 : dot product (add two 8x8)
1,0 : undefined (not use)


---------- for fixed point num multiply ----------
i_dotp, i_mode8 must be 0 (16x16 mode, not dot product)
i_sign : must be 1 (all the fixed point insn is signed) 
(i_round_q, i_msub_q) =>
0,X : no round
1,0 : round for mul_q/madd_q 
1,1 : round for msub_q 
======================================== */

module Mfxu_mul_mix ( /*AUTOARG*/
   // Outputs
   o_final_pp0, o_final_pp1, o_final_pp2, o_final_pp3, o_final_pp4,
   // Inputs
   i_a, i_b, i_c, i_sign, i_mode8, i_dotp, i_madd, i_msub, i_mode_q,
   i_round_q
   );
   
   // output logic [31:0] o_product;
   output logic [31:0] o_final_pp0, 
					   o_final_pp1,
					   o_final_pp2,
					   o_final_pp3,
					   o_final_pp4;		// final pp
   
   input logic [15:0]  i_a;						// multicand
   input logic [15:0]  i_b;						// multiplier
   input logic [15:0]  i_c;						// the third operand for madd/msub
   
   input logic 		   i_sign;					// sign or unsigned mode 
   input logic 		   i_mode8;					// 8x8 or 16x16 mode
   input logic 		   i_dotp;					// dot product mode
   input logic 		   i_madd;					// madd insn (only for select the third operand: i_c)
   input logic 		   i_msub;	// msub of normal/dotp/fixedpoint
   input logic 		   i_mode_q;	// fixed point mode
   input logic 		   i_round_q; // round for fixed point mode

   
   logic [15:0] 	   op_c;
   logic 			   round_q, msub_q;
   
   // assign op_c = i_madd ? i_c : i_msub ? ~i_c : '0;	//processed in the pre pipeline stage
   assign op_c = i_c;
   
   assign round_q = i_round_q;		// i_round_q ^ i_msub;
   assign msub_q = i_mode_q & i_msub;
				   
   
   wire 			   a_sign = i_sign & i_a[15];
   wire 			   b_sign = i_sign & i_b[15];
   wire 			   mode8 = i_mode8;

   
   wire [15+1:0] 		   a = {a_sign, i_a};
   wire [15+2:0] 		   b = {{2{b_sign}}, i_b};

   wire 				   a_16_sign = a_sign;
   wire 				   a_8_L_sign = i_sign & i_a[7];
   wire 				   a_8_H_sign = i_sign & i_a[15];
   wire 				   b_16_sign = b_sign;
   wire 				   b_8_L_sign = i_sign & i_b[7];
   wire 				   b_8_H_sign = i_sign & i_b[15];

   wire [15+1:0] 		   a_16 = a;
   wire [7+1:0] 		   a_8_L = {a_8_L_sign, i_a[7:0]};
   wire [7+1:0] 		   a_8_H = {a_8_H_sign, i_a[15:8]};

   wire [15+3:0] 		   b_16_ext = {b, 1'b0};
   wire [15+3:0] 		   b_8_L_ext = {b[17:8], b[7:0], 1'b0};
   wire [15+3:0] 		   b_8_H_ext = {b[17:8], 1'b0, b[6:0], 1'b0};
   wire [15+3:0] 		   b_mix_ext_0 = mode8 ? b_8_L_ext : b_16_ext;
   wire [15+3:0] 		   b_mix_ext_1 = mode8 ? b_8_H_ext : b_16_ext;

   wire [15+1:0] a_mix_0 = mode8 ? {a_8_L_sign, a[15:9], a_8_L_sign, a[7:0]} : a[16:0];
   wire [15+1:0] a_mix_1 = mode8 ? {a_8_H_sign, a[15:8], 1'b0, a[6:0]} : a[16:0];

   //PPs for common
   logic [15+1:0] pp[8:0];		// total 9 PPs
   logic 		  S[7:0];		// one less than the num of PPs
   logic 		  E[7:0];
   
   logic [31:0]   final_pp0, final_pp1, final_pp2, final_pp3, final_pp4; // one bit more than o_final_ppx
   

   // ==================== gen PPs, S, E for any mode ====================
   // low 4 PPs
   Mfxu_booth2_pp_gen #(16+1) mul_pp0
	 (
	  .pp_out(pp[0]),
	  .pp_s(S[0]),
	  .pp_e(E[0]),
	  .mcand(a_mix_0),			// multiplicand
	  .booth2(b_mix_ext_0[2:0])
	  );
   
   Mfxu_booth2_pp_gen #(16+1) mul_pp1
	 (
	  .pp_out(pp[1]),
	  .pp_s(S[1]),
	  .pp_e(E[1]),
	  .mcand(a_mix_0),
	  .booth2(b_mix_ext_0[4:2])
	  );
   
   Mfxu_booth2_pp_gen #(16+1) mul_pp2
	 (
	  .pp_out(pp[2]),
	  .pp_s(S[2]),
	  .pp_e(E[2]),
	  .mcand(a_mix_0),
	  .booth2(b_mix_ext_0[6:4])
	  );
   
   Mfxu_booth2_pp_gen #(16+1) mul_pp3
	 (
	  .pp_out(pp[3]),
	  .pp_s(S[3]),
	  .pp_e(E[3]),
	  .mcand(a_mix_0),
	  .booth2(b_mix_ext_0[8:6])
	  );


   // high 5 PPs
   Mfxu_booth2_pp_gen #(16+1) mul_pp4
	 (
	  .pp_out(pp[4]),
	  .pp_s(S[4]),
	  .pp_e(E[4]),
	  .mcand(a_mix_1),	
	  .booth2(b_mix_ext_1[10:8])
	  );
   Mfxu_booth2_pp_gen #(16+1) mul_pp5
	 (
	  .pp_out(pp[5]),
	  .pp_s(S[5]),
	  .pp_e(E[5]),
	  .mcand(a_mix_1),
	  .booth2(b_mix_ext_1[12:10])
	  );
   Mfxu_booth2_pp_gen #(16+1) mul_pp6
	 (
	  .pp_out(pp[6]),
	  .pp_s(S[6]),
	  .pp_e(E[6]),
	  .mcand(a_mix_1),	
	  .booth2(b_mix_ext_1[14:12])
	  );
   Mfxu_booth2_pp_gen #(16+1) mul_pp7
	 (
	  .pp_out(pp[7]),
	  .pp_s(S[7]),
	  .pp_e(E[7]),
	  .mcand(a_mix_1),
	  .booth2(b_mix_ext_1[16:14])
	  );
   Mfxu_booth2_pp_gen #(16+1) mul_pp8
	 (
	  .pp_out(pp[8]),
	  .pp_s(),					// not need
	  .pp_e(),
	  .mcand(a_mix_1),			// multiplicand
	  .booth2(b_mix_ext_1[18:16])
	  );
   
   // ==================== the PPs, S, E for 16x16 mode ====================
   logic [15+1:0] pp_16[8:0];
   logic 		  S_16[7:0];
   logic 		  E_16[7:0];
   
   genvar 		  i;
   generate
	  for(i=0;i<=8;i++)
		begin
		   assign pp_16[i] = pp[i];
		end
   endgenerate

   generate
	  for(i=0;i<=7;i++)
		begin
		   assign S_16[i] = S[i];
		   assign E_16[i] = E[i];
		end
   endgenerate
   
   // ==================== the PPs, S, E for 8x8 mode ====================
   logic [7+1:0] pp_8_L[4:0], pp_8_H[4:0];
   logic 		 S_8_L[3:0], S_8_H[3:0];
   logic 		 E_8_L[3:0], E_8_H[3:0];
   
   assign pp_8_L[0] = pp[0][8:0];
   assign pp_8_L[1] = pp[1][8:0];
   assign pp_8_L[2] = pp[2][8:0];
   assign pp_8_L[3] = pp[3][8:0];

   Mfxu_booth2_pp_gen #(8+1) mul_pp_8_L_4
	 (
	  .pp_out(pp_8_L[4]),
	  .pp_s(),					// not need
	  .pp_e(),
	  .mcand(a_8_L),
	  .booth2({{2{b_8_L_sign}}, b[7]})
	  );
   
   assign S_8_L[0] = S[0];
   assign S_8_L[1] = S[1];
   assign S_8_L[2] = S[2];
   assign S_8_L[3] = S[3];
   
   assign E_8_L[0] = E[0];
   assign E_8_L[1] = E[1];
   assign E_8_L[2] = E[2];
   assign E_8_L[3] = E[3];


   assign pp_8_H[0] = pp[4][16:8];
   assign pp_8_H[1] = pp[5][16:8];
   assign pp_8_H[2] = pp[6][16:8];
   assign pp_8_H[3] = pp[7][16:8];
   assign pp_8_H[4] = pp[8][16:8];

   assign S_8_H[0] = S[4];
   assign S_8_H[1] = S[5];
   assign S_8_H[2] = S[6];
   assign S_8_H[3] = S[7];
   
   assign E_8_H[0] = E[4];
   assign E_8_H[1] = E[5];
   assign E_8_H[2] = E[6];
   assign E_8_H[3] = E[7];
   
   
   // ==================== compress ====================
   logic [31:0]  pp_ext[10:0];
   assign pp_ext[0] = {E[7] , 1'b1 , E[6] , 1'b1 , E[5] , 1'b1 , E[4] , 1'b1 , pp[4][15] , pp[4][14] , pp[4][13] , pp[4][12] , pp[4][11] , pp[4][10] , pp[4][9] , pp[4][8] , (mode8 ? i_dotp : pp[0][15]) , pp[3][8] , pp[3][7] , pp[2][8] , pp[2][7] , pp[1][8] , pp[1][7] , pp[0][8] , (~i_mode_q & op_c[7]) , (~i_mode_q & op_c[6]) , (~i_mode_q & op_c[5]) , (~i_mode_q & op_c[4]) , (~i_mode_q & op_c[3]) , (~i_mode_q & op_c[2]) , (~i_mode_q & op_c[1]) , (~i_mode_q & op_c[0])};
   assign pp_ext[1] = {op_c[15] , pp[7][16] , pp[7][15] , pp[6][16] , pp[6][15] , pp[5][16] , pp[5][15] , pp[4][16] , pp[5][13] , pp[5][12] , pp[5][11] , pp[5][10] , pp[5][9] , pp[5][8] , (mode8 ? op_c[9] : {~E[0]}) , (mode8 ? S[4] : pp[0][16]) , (mode8 ? (i_dotp & E[7]) : pp[1][13]) , (mode8 ? (i_dotp & pp[7][16]) : pp[0][14]) , (mode8 ? i_dotp : pp[0][13]) , pp[3][6] , pp[3][5] , pp[2][6] , pp[2][5] , pp[1][6] , pp[0][7] , pp[0][6] , pp[0][5] , pp[0][4] , pp[0][3] , pp[0][2] , pp[0][1] , pp[0][0]};
   assign pp_ext[2] = {1'b0 , op_c[15] , op_c[14] , pp[7][14] , pp[7][13] , pp[6][14] , pp[6][13] , pp[5][14] , pp[6][11] , pp[6][10] , pp[6][9] , pp[6][8] , (mode8 ? op_c[11] : E[0]) , (mode8 ? S[5] : {~E[0]}) , (~mode8 & pp[1][15]) , (mode8 ? op_c[8] : pp[1][14]) , (mode8 ? (i_dotp & pp[8][15]) : pp[2][11]) , (mode8 ? (i_dotp & pp[8][14]) : pp[1][12]) , (mode8 ? (i_dotp & E[6]) : pp[1][11]) , (mode8 ? (i_dotp & pp[6][16]) : pp[0][12]) , (mode8 ? i_dotp : pp[0][11]) , pp[3][4] , pp[3][3] , pp[2][4] , pp[1][5] , pp[1][4] , pp[1][3] , pp[1][2] , pp[1][1] , pp[1][0] , (mode8 ? (i_dotp & pp[4][9]) : msub_q) , S[0]};
   assign pp_ext[3] = {3'b0 , op_c[13] , op_c[12] , pp[7][12] , pp[7][11] , pp[6][12] , pp[7][9] , pp[7][8] , (mode8 ? op_c[13] : E[2]) , (mode8 ? S[6] : 1'b1) , (~mode8 & E[1]) , (mode8 ? op_c[10] : pp[1][16]) , (~mode8 & pp[2][13]) , (~mode8 & pp[2][12]) , (mode8 ? (i_dotp & op_c[15]) : pp[3][9]) , (mode8 ? (i_dotp & op_c[14]) : pp[2][10]) , (mode8 ? (i_dotp & pp[7][15]) : pp[2][9]) , (mode8 ? (i_dotp & pp[7][14]) : pp[1][10]) , (mode8 ? (i_dotp & E[5]) : pp[1][9]) , (mode8 ? i_dotp : pp[0][10]) , (mode8 ? (i_dotp & E[4]) : pp[0][9]) , pp[3][2] , pp[2][3] , pp[2][2] , pp[2][1] , pp[2][0] , (mode8 ? (i_dotp & pp[4][11]) : msub_q) , S[1] , 1'b0 , (mode8 ? (i_dotp & pp[4][8]) : msub_q)};
   assign pp_ext[4] = {5'b0 , op_c[11] , op_c[10] , pp[7][10] , (mode8 ? op_c[15] : E[3]) , (mode8 ? S[7] : 1'b1) , (~mode8 & pp[3][15]) , (mode8 ? op_c[12] : pp[2][16]) , (~mode8 & pp[2][15]) , (~mode8 & pp[2][14]) , (~mode8 & pp[3][11]) , (~mode8 & pp[3][10]) , (mode8 ? E[3] : pp[4][7]) , (mode8 ? pp_8_L[4][6] : pp[4][6]) , (mode8 ? (i_dotp & pp[8][13]) : pp[4][5]) , (mode8 ? (i_dotp & pp[8][12]) : pp[4][4]) , (mode8 ? (i_dotp & pp[6][15]) : pp[4][3]) , (mode8 ? (i_dotp & pp[5][16]) : pp[4][2]) , (mode8 ? (i_dotp & pp[5][15]) : pp[4][1]) , (mode8 ? (i_dotp & pp[4][16]) : pp[4][0]) , pp[3][1] , pp[3][0] , (mode8 ? (i_dotp & pp[4][13]) : msub_q) , S[2] , (i_dotp & pp[5][9]) , (mode8 ? (i_dotp & pp[4][10]) : msub_q) , 1'b0 , (i_dotp & S[4])};
   assign pp_ext[5] = {7'b0 , op_c[9] , (~mode8 & op_c[8]) , (mode8 ? op_c[14] : pp[3][16]) , (~mode8 & pp[7][7]) , (~mode8 & pp[3][14]) , (~mode8 & pp[3][13]) , (~mode8 & pp[3][12]) , (~mode8 & pp[5][7]) , (~mode8 & pp[5][6]) , (mode8 ? pp_8_L[4][7] : pp[5][5]) , (~mode8 & pp[5][4]) , (mode8 ? (i_dotp & op_c[13]) : pp[5][3]) , (mode8 ? (i_dotp & op_c[12]) : pp[5][2]) , (mode8 ? (i_dotp & pp[7][13]) : pp[5][1]) , (mode8 ? (i_dotp & pp[6][14]) : pp[5][0]) , (mode8 ? (i_dotp & pp[6][13]) : (~i_mode_q & op_c[9])) , (mode8 ? (i_dotp & pp[5][14]) : S[4]) , (mode8 ? (i_dotp & pp[4][15]) : msub_q) , S[3] , (i_dotp & pp[5][11]) , (mode8 ? (i_dotp & pp[4][12]) : msub_q) , 1'b0 , (i_dotp & pp[5][8]) , 2'b0};
   assign pp_ext[6] = {9'b0 , (~mode8 & op_c[7]) , (~mode8 & op_c[6]) , (~mode8 & pp[7][6]) , (~mode8 & pp[6][7]) , (~mode8 & pp[6][6]) , (~mode8 & pp[6][5]) , (~mode8 & pp[6][4]) , (~mode8 & pp[6][3]) , (~mode8 & pp[6][2]) , (mode8 ? E[2] : pp[6][1]) , (mode8 ? pp_8_L[4][4] : pp[6][0]) , (mode8 ? (i_dotp & pp[8][11]) : (~i_mode_q & op_c[11])) , (mode8 ? (i_dotp & pp[7][12]) : S[5]) , (mode8 ? (i_dotp & pp[7][11]) : msub_q) , (mode8 ? (i_dotp & pp[6][12]) : (~i_mode_q & op_c[8])) , (i_dotp & pp[5][13]) , (mode8 ? (i_dotp & pp[4][14]) : msub_q) , (i_dotp & pp[6][9]) , (i_dotp & pp[5][10]) , 1'b0 , (i_dotp & S[5]) , 2'b0};
   assign pp_ext[7] = {11'b0 , (~mode8 & op_c[5]) , (~mode8 & pp[7][5]) , (~mode8 & pp[7][4]) , (~mode8 & pp[7][3]) , (~mode8 & pp[7][2]) , (~mode8 & pp[7][1]) , (~mode8 & pp[7][0]) , (mode8 ? pp_8_L[4][5] : (~i_mode_q & op_c[13])) , (~mode8 & S[6]) , (mode8 ? (i_dotp & op_c[11]) : msub_q) , (mode8 ? (i_dotp & pp[8][10]) : (~i_mode_q & op_c[10])) , (i_dotp & pp[8][9]) , (mode8 ? (i_dotp & pp[7][10]) : msub_q) , (i_dotp & pp[6][11]) , (i_dotp & pp[5][12]) , 1'b0 , (i_dotp & pp[6][8]) , 4'b0};
   assign pp_ext[8] = {12'b0 , (~mode8 & op_c[4]) , (~mode8 & op_c[3]) , (~mode8 & op_c[2]) , (~mode8 & op_c[1]) , (~mode8 & (~i_mode_q & op_c[15])) , (~mode8 & S[7]) , (~mode8 & msub_q) , (~mode8 & (~i_mode_q & op_c[12])) , (mode8 & E[1]) , (mode8 ? (i_dotp & op_c[10]) : msub_q) , (i_dotp & op_c[9]) , (i_dotp & pp[8][8]) , (i_dotp & pp[7][9]) , (i_dotp & pp[6][10]) , 1'b0 , (i_dotp & S[6]) , 4'b0};
   assign pp_ext[9] = {16'b0 , (~mode8 & (i_mode_q & op_c[0])) , (~mode8 & (~i_mode_q & op_c[14])) , 1'b0 , (~mode8 & msub_q) , (mode8 & pp_8_L[4][3]) , (mode8 & pp_8_L[4][2]) , (mode8 & E[0]) , (i_dotp & op_c[8]) , 1'b0 , (i_dotp & pp[7][8]) , 6'b0};
assign pp_ext[10] = {17'b0 , (~mode8 & (round_q^msub_q)) , 4'b0 , (mode8 & pp_8_L[4][1]) , (mode8 & pp_8_L[4][0]) , 1'b0 , (i_dotp & S[7]) , 6'b0};

   
   
   
   // wire [11*32-1:0] tree_in = { pp_ext[0], pp_ext[1], pp_ext[2], pp_ext[3], pp_ext[4], pp_ext[5], pp_ext[6], pp_ext[7], pp_ext[8], pp_ext[9], pp_ext[10]};
   
   // DW02_tree #( .num_inputs(11),
   // 				.input_width(32) 
   // 				) U_wallace (
   // 							 .INPUT(tree_in),
   // 							 .OUT0(final_pp0),
   // 							 .OUT1(final_pp1) );


   DW02_tree #( .num_inputs(4),
   				.input_width(32) 
   				) U_wallace1 (
   							 .INPUT({pp_ext[0], pp_ext[1], pp_ext[2], pp_ext[3]/*, pp_ext[4]*/}),
   							 .OUT0(final_pp0),
   							 .OUT1(final_pp1) );

   DW02_tree #( .num_inputs(6),
   				.input_width(32) 
   				) U_wallace2 (
   							 .INPUT({pp_ext[4], pp_ext[5], pp_ext[6], pp_ext[7], pp_ext[8], pp_ext[9]/*, pp_ext[10]*/}),
   							 .OUT0(final_pp2),
   							 .OUT1(final_pp3) );
   
   
   assign o_final_pp0 = final_pp0;
   assign o_final_pp1 = final_pp1;
   assign o_final_pp2 = final_pp2;
   assign o_final_pp3 = final_pp3;
   assign o_final_pp4 = pp_ext[10];
   
endmodule // Mfxu_mul_mix



